﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Windows;
using System.Xml;

namespace OfficeOpenXml
{
    public class ConditionalFormatting
    {
        private XmlNode _xmlNode;
        private ExcelWorksheet _worksheet;

        public ConditionalFormatting(ExcelWorksheet _worksheet)
        {
            this._worksheet = _worksheet;

            XmlElement element = this.Worksheet.WorksheetXml.CreateElement("conditionalFormatting",
                                                                           ExcelPackage.schemaMain);
            element.SetAttribute("sqref", "A1");
            _xmlNode = element;
            this._worksheet.WorksheetXml.DocumentElement.AppendChild(_xmlNode);

        }

        public ConditionalFormatting(ExcelWorksheet _worksheet, XmlNode _xmlNode)
        {
            this._worksheet = _worksheet;
            this._xmlNode = _xmlNode;
            if (_xmlNode.FirstChild != null)
            {
                Rule = new ConditionalFormattingRule(this, _xmlNode.FirstChild);   
            }            
        }

        public XmlNode XmlNode
        {
            get { return this._xmlNode; }
        }
        
        
        /// <summary>
        /// Address cell or cell range that conditional formatting will be applied to
        /// i.e. H17 or C3:C8
        /// </summary>
        public string CellAddress
        {
            get
            {
                return XmlNode.Attributes["sqref"].Value;
            }
            set { XmlNode.Attributes["sqref"].Value = value; }
        }

        public ExcelWorksheet Worksheet
        {
            get { return this._worksheet; }
        }

        public ConditionalFormattingRule Rule
        {
            get; set;
        }
    }

    public abstract class RuleVisualizer 
    {
        protected XmlNode _xmlNode;
        private XmlNode _minNode;
        private XmlNode _maxNode;
        private ConditionalFormattingRule conditionalFormattingRule;

        protected RuleVisualizer(ConditionalFormattingRule conditionalFormatting)
        {
            this.conditionalFormattingRule = conditionalFormatting;
        }

        protected RuleVisualizer(ConditionalFormattingRule conditionalFormatting, XmlNode ruleNode)
        {
            this.conditionalFormattingRule = conditionalFormatting;
            foreach (XmlNode child in ruleNode.ChildNodes)
            {
                if (child.Name == "cfvo")
                {
                    XmlAttribute typeAttribute = child.Attributes["type"];
                    if (typeAttribute.Value == "min")
                    {
                        this.Min = int.Parse(typeAttribute.Value);
                    }
                    else if (typeAttribute.Value == "max")
                    {
                        this.Max = int.Parse(typeAttribute.Value);
                    }
                    else
                    {
                        throw new Exception(string.Format("Unknown type {0}", typeAttribute.Value));
                    }
                }
            }
            this.conditionalFormattingRule = this.conditionalFormattingRule;
        }        

        public ConditionalFormattingRule ConditionalFormattingRule
        {
            get
            {
                return this.conditionalFormattingRule;
            }
        }

        public double Min
        {
            get
            {                
                if (_minNode == null)
                {
                    _minNode = this.GetMinNode();
                }
                return int.Parse(_minNode.Attributes["val"].Value);
            }
            set
            {
                if (_minNode == null)
                {
                    _minNode = this.GetMinNode();
                }
                _minNode.Attributes["val"].Value = value.ToString(CultureInfo.InvariantCulture);
            }
        }

        public double Max
        {
            get
            {                
                if (_maxNode == null)
                {
                    _maxNode = this.GetMaxNode();
                }
                return int.Parse(_maxNode.Attributes["val"].Value);
            }
            set
            {
                if (_maxNode == null)
                {
                    _maxNode = this.GetMaxNode();
                }
                _maxNode.Attributes["val"].Value = value.ToString(CultureInfo.InvariantCulture);
            }
        }

        private XmlNode GetMinNode()
        {
            XmlNode node = null;
            foreach (XmlNode childNode in _xmlNode.ChildNodes)
            {
                if (childNode.Name == "cfvo" && childNode.Attributes["type"].Value == "min")
                {
                    node = childNode;
                    break;
                }
            }

            if (node == null)
            {
                XmlElement element =
                    this.ConditionalFormattingRule.ConditionalFormatting.Worksheet.WorksheetXml.CreateElement("cfvo",
                                                                                                              ExcelPackage
                                                                                                                  .
                                                                                                                  schemaMain);
                element.SetAttribute("type", "min");
                element.SetAttribute("val", "0");
                node = element;
                XmlNode.AppendChild(node);
            }
            return node;
        }

        private XmlNode GetMaxNode()
        {
            XmlNode node = null;
            foreach (XmlNode childNode in _xmlNode.ChildNodes)
            {
                if (childNode.Name == "cfvo" && childNode.Attributes["type"].Value == "max")
                {
                    node = childNode;
                    break;
                }
            }
            if (node == null)
            {

                XmlElement element =
                    this.ConditionalFormattingRule.ConditionalFormatting.Worksheet.WorksheetXml.CreateElement("cfvo",
                                                                                                              ExcelPackage
                                                                                                                  .
                                                                                                                  schemaMain);
                element.SetAttribute("type", "max");
                element.SetAttribute("val", "0");
                node = element;
                XmlNode.AppendChild(node);
            }
            return node;            
        }

        public XmlNode XmlNode
        {
            get { return this._xmlNode; }
        }

        
    }

    public class ConditionalFormattingRule
    {
        private XmlNode _xmlNode;
        private ConditionalFormatting _conditionalFormatting;
        private static readonly Dictionary<RuleType, string> _ruleTypeStringDictionary =
            new Dictionary<RuleType, string>();        
        public int Priority { get; set; }
        public int Rank { get; set; }
        public string Formula { get; set; }
        public RuleVisualizer RuleVisualizer { get; set; }

        static ConditionalFormattingRule()
        {
            _ruleTypeStringDictionary.Add(RuleType.Excpression, "expression");
            _ruleTypeStringDictionary.Add(RuleType.CellIs, "cellIs");
            _ruleTypeStringDictionary.Add(RuleType.ColorScale, "colorScale");
            _ruleTypeStringDictionary.Add(RuleType.DataBar, "dataBar");
            _ruleTypeStringDictionary.Add(RuleType.IconSet, "iconSet");
            _ruleTypeStringDictionary.Add(RuleType.Top10, "top10");
            _ruleTypeStringDictionary.Add(RuleType.UniqueValues, "uniqueValues");
            _ruleTypeStringDictionary.Add(RuleType.DuplicateValues, "duplicateValues");
            _ruleTypeStringDictionary.Add(RuleType.ContainsText, "containsText");
            _ruleTypeStringDictionary.Add(RuleType.NotContainsText, "notContainsText");
            _ruleTypeStringDictionary.Add(RuleType.BeginWith, "beginsWith");
            _ruleTypeStringDictionary.Add(RuleType.EndsWith, "endsWith");
            _ruleTypeStringDictionary.Add(RuleType.ContainsBlanks, "containsBlanks");
            _ruleTypeStringDictionary.Add(RuleType.NotContainsBlanks, "notContainsBlanks");
            _ruleTypeStringDictionary.Add(RuleType.ContainsErrors, "containsErrors");
            _ruleTypeStringDictionary.Add(RuleType.NotContainsErrors, "notContainsErrors");
            _ruleTypeStringDictionary.Add(RuleType.TimePeriod, "timePeriod");
            _ruleTypeStringDictionary.Add(RuleType.AboveAvarage, "aboveAverage");                                            
        }

        public ConditionalFormattingRule(ConditionalFormatting conditionalFormatting)
        {
            this._conditionalFormatting = conditionalFormatting;
            XmlElement element = this.ConditionalFormatting.Worksheet.WorksheetXml.CreateElement("cfRule", ExcelPackage.schemaMain);
            element.SetAttribute("type", "dataBar");
            element.SetAttribute("priority", this.ConditionalFormatting.Worksheet.ConditionalFormattings.Count.ToString());
            
            _xmlNode = element;
            ConditionalFormatting.XmlNode.AppendChild(_xmlNode);
        }       

        public ConditionalFormattingRule(ConditionalFormatting conditionalFormatting, XmlNode _xmlNode)
        {
            this._conditionalFormatting = conditionalFormatting;
            this._xmlNode = _xmlNode;
            if (XmlNode.FirstChild != null)
            {
                XmlNode ruleNode = XmlNode.FirstChild;
                switch (this.Type)
                {
                    case RuleType.DataBar:
                        if (ruleNode.FirstChild.Name != "dataBar")
                        {
                            throw new Exception("Expecting dataBar");
                        }
                        this.RuleVisualizer = new DataBar(this, ruleNode.FirstChild);
                        break;
                    case RuleType.ColorScale:
                        if (ruleNode.FirstChild.Name != "colorScale")
                        {
                            throw new Exception("Expecting colorScale");
                        }
                        this.RuleVisualizer = new ColorScale(this, ruleNode.FirstChild);
                        break;
                    default:
                        throw new NotImplementedException();
                }
            }
        }

        public ConditionalFormatting ConditionalFormatting
        {
            get
            {
                return _conditionalFormatting;
            }
        }

        public XmlNode XmlNode
        {
            get
            {
                return this._xmlNode;
            }
        }

        public RuleType Type
        {
            get
            {
                return GetRuleType(_xmlNode.Attributes["type"].Value);
            }
            set
            {
                _xmlNode.Attributes["type"].Value = _ruleTypeStringDictionary[value];
            }
        }

        private static RuleType GetRuleType(string ruleTypeString)
        {
            foreach (var pair in _ruleTypeStringDictionary)
            {
                if (pair.Value == ruleTypeString)
                {
                    return pair.Key;
                }
            }
            throw new Exception(string.Format("Don't know to parse {0}", ruleTypeString));
        }

        public string GetRuleTypeString(RuleType type)
        {
            if (!_ruleTypeStringDictionary.ContainsKey(type))
            {
                throw new Exception(string.Format("Unkown rule type {0}", type));
            }
            return _ruleTypeStringDictionary[type];
        }
    }

    public enum RuleType
    {
        AboveAvarage,
        BeginWith,
        CellIs,
        ColorScale,
        ContainsBlanks,
        ContainsErrors,
        ContainsText,
        DataBar,
        DuplicateValues,
        EndsWith,
        Excpression,
        IconSet,
        NotContainsBlanks,
        NotContainsErrors,
        NotContainsTime,
        NotContainsText,
        TimePeriod,
        Top10,
        UniqueValues
    }
    
}
